﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.ServiceModel.DomainServices;
    using System.Text;
    using System.Diagnostics.CodeAnalysis;

    internal static class CustomAttributeGenerator
    {
        private static Dictionary<Type, ICustomAttributeBuilder> _knownBuilders = new Dictionary<Type, ICustomAttributeBuilder>();
        private static Dictionary<Type, Type> _knownBuilderTypes;
        private static readonly Type[] blockList = new Type[] { typeof(MetadataTypeAttribute), typeof(ScaffoldColumnAttribute), typeof(ScaffoldTableAttribute), typeof(SerializableAttribute), typeof(SuppressMessageAttribute) };

        private static CodeCommentStatementCollection ConstructCodeAttributeFailureComments(string errorMessage)
        {
            CodeCommentStatementCollection statements = new CodeCommentStatementCollection();
            statements.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_FailedToGenerate_ErrorTemplate, new object[] { errorMessage })));
            statements.Add(new CodeCommentStatement(string.Empty));
            return statements;
        }

        private static CodeCommentStatementCollection ConstructCodeAttributeFailureComments(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration)
        {
            CodeCommentStatementCollection statements = new CodeCommentStatementCollection();
            foreach (string str in attributeDeclaration.Errors)
            {
                statements.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_FailedToGenerate_ErrorTemplate, new object[] { str })));
            }
            statements.Add(new CodeCommentStatement(GenerateCodeAttribute(proxyGenerator, attributeDeclaration)));
            statements.Add(new CodeCommentStatement(string.Empty));
            return statements;
        }

        private static string ConvertValueToCode(object value, bool isCSharp)
        {
            if (value == null)
            {
                if (!isCSharp)
                {
                    return "Nothing";
                }
                return "null";
            }
            if (value is string)
            {
                return ("\"" + value.ToString() + "\"");
            }
            if (value is char)
            {
                return ("'" + value.ToString() + "'");
            }
            if (value is Type)
            {
                string format = isCSharp ? "typeof({0})" : "GetType({0})";
                return string.Format(CultureInfo.CurrentCulture, format, new object[] { value });
            }
            return value.ToString();
        }

        private static CodeAttributeDeclaration CreateCodeAttributeDeclaration(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, AttributeDeclaration attributeDeclaration)
        {
            CodeAttributeDeclaration declaration = CodeGenUtilities.CreateAttributeDeclaration(attributeDeclaration.AttributeType, proxyGenerator, referencingType);
            foreach (object obj2 in attributeDeclaration.ConstructorArguments)
            {
                CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, obj2);
                declaration.Arguments.Add(new CodeAttributeArgument(expression));
            }
            foreach (KeyValuePair<string, object> pair in attributeDeclaration.NamedParameters)
            {
                CodeExpression expression2 = CreateCodeExpression(proxyGenerator, referencingType, pair.Value);
                declaration.Arguments.Add(new CodeAttributeArgument(pair.Key, expression2));
            }
            return declaration;
        }

        private static CodeExpression CreateCodeExpression(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, object value)
        {
            string fullName;
            Type enumType = (value == null) ? null : value.GetType();
            if (((value == null) || enumType.IsPrimitive) || (value is string))
            {
                CodeExpression expression = new CodePrimitiveExpression(value);
                if ((value == null) || (!(value is double) && !(value is float)))
                {
                    return expression;
                }
                return new CodeCastExpression(value.GetType(), expression);
            }
            Type type = value as Type;
            if (type != null)
            {
                if ((proxyGenerator.GetTypeShareKind(type) & CodeMemberShareKind.Shared) == CodeMemberShareKind.Unknown)
                {
                    CodeTypeReference reference = CodeGenUtilities.GetTypeReference(type, proxyGenerator, referencingType, false, true);
                    reference.Options = CodeTypeReferenceOptions.GlobalReference;
                    return new CodeTypeOfExpression(reference);
                }
                return new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(type, proxyGenerator, referencingType));
            }
            if (!enumType.IsEnum)
            {
                return null;
            }
            string name = Enum.GetName(enumType, value);
            if (proxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames)
            {
                fullName = enumType.FullName;
            }
            else
            {
                fullName = enumType.Name;
            }
            return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(fullName), name);
        }

        private static string GenerateCodeAttribute(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration)
        {
            StringBuilder builder = new StringBuilder();
            bool isCSharp = proxyGenerator.IsCSharp;
            builder.Append(isCSharp ? '[' : '<');
            builder.Append(attributeDeclaration.AttributeType.Name);
            builder.Append('(');
            if (attributeDeclaration.ConstructorArguments.Count > 0)
            {
                foreach (object obj2 in attributeDeclaration.ConstructorArguments)
                {
                    builder.Append(ConvertValueToCode(obj2, isCSharp));
                    builder.Append(", ");
                }
            }
            if (attributeDeclaration.NamedParameters.Count > 0)
            {
                foreach (KeyValuePair<string, object> pair in attributeDeclaration.NamedParameters)
                {
                    builder.Append(pair.Key);
                    builder.Append(isCSharp ? " = " : " := ");
                    builder.Append(ConvertValueToCode(pair.Value, isCSharp));
                    builder.Append(", ");
                }
            }
            if ((attributeDeclaration.ConstructorArguments.Count > 0) || (attributeDeclaration.NamedParameters.Count > 0))
            {
                builder.Remove(builder.Length - 2, 2);
            }
            builder.Append(')');
            builder.Append(isCSharp ? "]" : "> _");
            return builder.ToString();
        }

        public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, bool forcePropagation)
        {
            GenerateCustomAttributes(proxyGenerator, referencingType, null, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate, forcePropagation);
        }

        public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, System.Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments)
        {
            GenerateCustomAttributes(proxyGenerator, referencingType, getLogWarningMessage, attributes, outputCollection, comments, Resource.ClientCodeGen_Attribute_FailedToGenerate);
        }

        public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, System.Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, string customCommentHeader)
        {
            GenerateCustomAttributes(proxyGenerator, referencingType, getLogWarningMessage, attributes, outputCollection, comments, customCommentHeader, false);
        }

        private static IEnumerable<CodeAttributeDeclaration> GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, System.Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeCommentStatementCollection comments, string customCommentHeader, bool forcePropagation)
        {
            bool emittedErrorCommentHeader = false;
            List<CodeAttributeDeclaration> list = new List<CodeAttributeDeclaration>(attributes.Count<Attribute>());
            foreach (Attribute attribute in from a in attributes
                orderby a.GetType().Name
                select a)
            {
                Type attributeType = attribute.GetType();
                if (!IsAttributeBlocked(attributeType))
                {
                    bool flag2 = false;
                    bool flag3 = string.Equals(attributeType.Namespace, typeof(ValidationAttribute).Namespace, StringComparison.Ordinal);
                    ICustomAttributeBuilder customAttributeBuilder = GetCustomAttributeBuilder(attributeType);
                    if (customAttributeBuilder != null)
                    {
                        AttributeDeclaration attributeDeclaration = null;
                        try
                        {
                            attributeDeclaration = customAttributeBuilder.GetAttributeDeclaration(attribute);
                        }
                        catch (AttributeBuilderException exception)
                        {
                            GenerateCustomAttributesErrorCommentHeader(comments, customCommentHeader, ref emittedErrorCommentHeader);
                            comments.AddRange(ConstructCodeAttributeFailureComments(exception.Message));
                            if (getLogWarningMessage != null)
                            {
                                string message = getLogWarningMessage(exception);
                                proxyGenerator.LogWarning(message);
                            }
                            continue;
                        }
                        if (attributeDeclaration != null)
                        {
                            if (!forcePropagation)
                            {
                                ValidateAttributeDeclarationRequirements(proxyGenerator, attributeDeclaration);
                            }
                            if (attributeDeclaration.HasErrors)
                            {
                                if (flag3)
                                {
                                    GenerateCustomAttributesErrorCommentHeader(comments, customCommentHeader, ref emittedErrorCommentHeader);
                                    comments.AddRange(ConstructCodeAttributeFailureComments(proxyGenerator, attributeDeclaration));
                                }
                            }
                            else
                            {
                                CodeAttributeDeclaration item = CreateCodeAttributeDeclaration(proxyGenerator, referencingType, attributeDeclaration);
                                list.Add(item);
                                flag2 = true;
                            }
                        }
                    }
                    if (!flag2)
                    {
                        if (flag3)
                        {
                            if (proxyGenerator.GetTypeShareKind(attributeType) == CodeMemberShareKind.NotShared)
                            {
                                proxyGenerator.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresDataAnnotations, new object[] { attributeType, proxyGenerator.ClientProjectName }));
                            }
                            continue;
                        }
                        if ((customAttributeBuilder == null) && typeof(ValidationAttribute).IsAssignableFrom(attributeType))
                        {
                            proxyGenerator.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresBuilder, new object[] { attributeType }));
                        }
                    }
                }
            }
            list.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
            return list;
        }

        public static void GenerateCustomAttributes(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, System.Func<AttributeBuilderException, string> getLogWarningMessage, IEnumerable<Attribute> attributes, CodeAttributeDeclarationCollection outputCollection, CodeCommentStatementCollection comments, string customCommentHeader, bool forcePropagation)
        {
            foreach (CodeAttributeDeclaration declaration in GenerateCustomAttributes(proxyGenerator, referencingType, getLogWarningMessage, attributes, comments, customCommentHeader, forcePropagation))
            {
                outputCollection.Add(declaration);
            }
        }

        private static void GenerateCustomAttributesErrorCommentHeader(CodeCommentStatementCollection comments, string customCommentHeader, ref bool emittedErrorCommentHeader)
        {
            if (!emittedErrorCommentHeader)
            {
                comments.Add(new CodeCommentStatement(customCommentHeader));
                comments.Add(new CodeCommentStatement(string.Empty));
                emittedErrorCommentHeader = true;
            }
        }

        private static ICustomAttributeBuilder GetCustomAttributeBuilder(Type attributeType)
        {
            if (attributeType == null)
            {
                throw new ArgumentNullException("attributeType");
            }
            ICustomAttributeBuilder builder = null;
            if (!KnownBuilders.TryGetValue(attributeType, out builder))
            {
                Type type = null;
                if (!KnownBuilderTypes.TryGetValue(attributeType, out type))
                {
                    foreach (KeyValuePair<Type, Type> pair in KnownBuilderTypes)
                    {
                        if (pair.Key.IsAssignableFrom(attributeType))
                        {
                            type = pair.Value;
                            break;
                        }
                    }
                }
                if (type == null)
                {
                    type = typeof(StandardCustomAttributeBuilder);
                }
                if (type != null)
                {
                    builder = Activator.CreateInstance(type) as ICustomAttributeBuilder;
                }
                if (builder != null)
                {
                    KnownBuilders[attributeType] = builder;
                }
            }
            return builder;
        }

        private static bool IsAttributeBlocked(Type attributeType)
        {
            return blockList.Contains<Type>(attributeType);
        }

        private static void ValidateAttributeDeclarationRequirements(CodeDomClientCodeGenerator proxyGenerator, AttributeDeclaration attributeDeclaration)
        {
            switch (proxyGenerator.GetTypeShareKind(attributeDeclaration.AttributeType))
            {
                case CodeMemberShareKind.Unknown:
                    attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_NoPDB, new object[] { attributeDeclaration.AttributeType, attributeDeclaration.AttributeType.Assembly.GetName().Name, proxyGenerator.ClientProjectName }));
                    break;

                case CodeMemberShareKind.NotShared:
                    attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared, new object[] { attributeDeclaration.AttributeType, proxyGenerator.ClientProjectName }));
                    break;
            }
            if (CS__CachedAnonymousMethodDelegate7 == null)
            {
                //CS__CachedAnonymousMethodDelegate7 = t => t.FullName;
            }
            //foreach (Type type in Enumerable.OrderBy<Type, string>(attributeDeclaration.RequiredTypes, CS__CachedAnonymousMethodDelegate7))
            //{
            //    switch (proxyGenerator.GetTypeShareKind(type))
            //    {
            //        case CodeMemberShareKind.Unknown:
            //        {
            //            attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Type_NoPDB, new object[] { attributeDeclaration.AttributeType, type, type.Assembly.GetName().Name, proxyGenerator.ClientProjectName }));
            //            continue;
            //        }
            //        case CodeMemberShareKind.NotShared:
            //            attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Type, new object[] { attributeDeclaration.AttributeType, type, proxyGenerator.ClientProjectName }));
            //            break;
            //    }
            //}
            foreach (MethodInfo info in from p in attributeDeclaration.RequiredMethods
                orderby p.Name
                select p)
            {
                if (proxyGenerator.GetMethodShareKind(info) == CodeMemberShareKind.NotShared)
                {
                    attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Method, new object[] { attributeDeclaration.AttributeType, info.Name, info.DeclaringType, proxyGenerator.ClientProjectName }));
                }
            }
            foreach (PropertyInfo info2 in from p in attributeDeclaration.RequiredProperties
                orderby p.Name
                select p)
            {
                if (proxyGenerator.GetPropertyShareKind(info2.DeclaringType, info2.Name) == CodeMemberShareKind.NotShared)
                {
                    attributeDeclaration.Errors.Add(string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_RequiresShared_Property, new object[] { attributeDeclaration.AttributeType, info2.Name, info2.DeclaringType, proxyGenerator.ClientProjectName }));
                }
            }
        }

        private static Dictionary<Type, ICustomAttributeBuilder> KnownBuilders
        {
            get
            {
                return _knownBuilders;
            }
        }

        private static Dictionary<Type, Type> KnownBuilderTypes
        {
            get
            {
                if (_knownBuilderTypes == null)
                {
                    _knownBuilderTypes = new Dictionary<Type, Type>();
                    _knownBuilderTypes[typeof(CustomValidationAttribute)] = typeof(CustomValidationCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(DataMemberAttribute)] = typeof(DataMemberAttributeBuilder);
                    _knownBuilderTypes[typeof(DisplayAttribute)] = typeof(DisplayCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(DomainIdentifierAttribute)] = typeof(DomainIdentifierAttributeBuilder);
                    _knownBuilderTypes[typeof(EditableAttribute)] = typeof(EditableAttributeBuilder);
                    _knownBuilderTypes[typeof(RangeAttribute)] = typeof(RangeCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(RegularExpressionAttribute)] = typeof(ValidationCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(RequiredAttribute)] = typeof(ValidationCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(StringLengthAttribute)] = typeof(ValidationCustomAttributeBuilder);
                    _knownBuilderTypes[typeof(UIHintAttribute)] = typeof(UIHintCustomAttributeBuilder);
                }
                return _knownBuilderTypes;
            }
        }

        public static Func<MethodInfo> CS__CachedAnonymousMethodDelegate7 { get; set; }
    }
}

